4

简单记录下平时开发对类型断言(Type Assertion)的使用场景。

golang里的所有类型都实现了空接口interface{},所以通常将它作为一个函数的参数或者结构体的字段,以实现对类型的抽象。

1.用于转换函数里interface{}类型的参数
举个简单栗子:

package main

import "fmt"

func main() {
    add(1, 2)
    add(int16(1), int16(2))
    add(float32(1.1), float32(2.2))
    add(float64(1.1), float64(2.2))
    add(true, false)
}

func add(a, b interface{}) {
    switch t := a.(type) {
    case int:
        fmt.Printf("type [%T] add res[%d]\n", t, a.(int)+b.(int))
    case int16:
        fmt.Printf("type [%T] add res[%d]\n", t, a.(int16)+b.(int16))
    case float32:
        fmt.Printf("type [%T] add res[%f]\n", t, a.(float32)+b.(float32))
    case float64:
        fmt.Printf("type [%T] add res[%f]\n", t, a.(float64)+b.(float64))
    default:
        fmt.Printf("type [%T] not support!\n", t)
    }
}

输出结果:

type [int] add res[3]
type [int16] add res[3]
type [float32] add res[3.300000]
type [float64] add res[3.300000]
type [bool] not support!

用interface{}作参数,是不是很像C++的模板函数,而类型断言是不是很像C++的类层次间的下行转换(也是不一定成功的)。需要注意的是,a.(type)只能和switch搭配使用。在使用前得用断言指明变量的类型,如果断言错误就会触发panic。

如果不想触发panic,先做判断再使用。

package main

import "fmt"

func main() {
    a := int16(2)
    b := int32(3)
    add(a, b)
}

func add(a, b interface{}) {
    _, ok := a.(int32)
    if !ok {
        fmt.Println("error type assertion!")
    }
    b = b
}

运行结果:
error type assertion!
2.用于转换结构体的interface{}类型字段
例如,我们写handler去接收消息,不可能每个发来的消息都写个函数去handle。利用空接口和类型断言的特性,就可以将业务抽象出来:

 package main
    
    import "fmt"
    import "time"
    
    type NetMsg struct {
        MsgID int16
        Data  interface{}
    }
    
    type Cat struct {
        name string
        age  int16
    }
    
    type Dog struct {
        name string
        age  int32
    }
    
    type human struct {
        name string
        age  int64
    }
    
    func main() {
        msg1 := NetMsg{1, Cat{"Qian", 1}}
        msg2 := NetMsg{2, Dog{"doge", 8}}
        msg3 := NetMsg{3, Dog{"allu", 18}}
        msg_handler(msg1)
        time.Sleep(2000 * time.Millisecond)
        msg_handler(msg2)
        time.Sleep(2000 * time.Millisecond)
        msg_handler(msg3)
    }
    
    func msg_handler(msg NetMsg) {
        switch msg.MsgID {
        case 1:
            cat := msg.Data.(Cat)
            fmt.Printf("Do Something with Msg 1 %v \n", cat)
        case 2:
            dog := msg.Data.(Dog)
            fmt.Printf("Do Something with Msg 2 %v \n", dog)
        default:
            fmt.Printf("Error MsgID [%d] \n", msg.MsgID)
        }
    }

运行结果:
Do Something with Msg 1 {Qian 1}
Do Something with Msg 2 {doge 8}
Error MsgID [3]


Cedrus
363 声望9 粉丝

不积跬步,无以致千里